from flask import Flask, render_template, jsonify, request, send_from_directory, redirect, url_for
from flask_cors import CORS
from backend.database import db
import threading
import asyncio
import os
import sys
import random
import string
import time
from datetime import datetime
from backend.models.user import User

# 存储验证码的字典（实际项目中应使用Redis等存储）
verification_codes = {}
# 存储微信扫码登录会话的字典
wechat_login_sessions = {}
# 存储用户token的字典（实际项目中应使用Redis或数据库存储）
user_tokens = {}

# 不需要登录验证的路由
exempt_routes = [
    '/login',
    '/register',
    '/api/login',
    '/api/register',
    '/api/send_verification_code',
    '/api/wechat/qrcode',
    '/api/wechat/login_status/',  # 注意这里的末尾斜杠
    '/static/'  # 静态资源文件不需要登录
]

# 初始化Flask应用
app = Flask(__name__, template_folder='frontend/templates')
CORS(app)

# 登录验证装饰器
def login_required(f):
    from functools import wraps
    @wraps(f)
    def decorated_function(*args, **kwargs):
        # 检查请求路径是否在免验证列表中
        path = request.path
        for exempt_route in exempt_routes:
            if path.startswith(exempt_route):
                return f(*args, **kwargs)
        
        # 获取token
        token = request.cookies.get('token')
        if not token:
            # 对于API请求，返回JSON错误
            if path.startswith('/api/'):
                return jsonify({'success': False, 'message': '请先登录'}), 401
            # 对于页面请求，重定向到登录页
            return redirect(url_for('login'))
        
        # 验证token
        user_id = user_tokens.get(token)
        if not user_id:
            # 对于API请求，返回JSON错误
            if path.startswith('/api/'):
                return jsonify({'success': False, 'message': '登录已过期，请重新登录'}), 401
            # 对于页面请求，重定向到登录页
            return redirect(url_for('login'))
        
        # 将用户ID添加到请求上下文中，方便后续使用
        request.user_id = user_id
        
        return f(*args, **kwargs)
    return decorated_function

# 创建一个无参数的函数用于before_request
def before_request_handler():
    # 获取当前请求的路径
    path = request.path
    # 检查路径是否在免验证列表中
    if path in exempt_routes or any(path.startswith(exempt.replace('*', '')) for exempt in exempt_routes if '*' in exempt):
        return None
    
    # 从cookie中获取token
    token = request.cookies.get('token')
    if not token:
        # 对于API请求，返回JSON错误
        if path.startswith('/api/'):
            return jsonify({'success': False, 'message': '请先登录'}), 401
        # 对于页面请求，重定向到登录页
        return redirect(url_for('login'))
    
    # 验证token
    user_id = user_tokens.get(token)
    if not user_id:
        # 对于API请求，返回JSON错误
        if path.startswith('/api/'):
            return jsonify({'success': False, 'message': '登录已过期，请重新登录'}), 401
        # 对于页面请求，重定向到登录页
        return redirect(url_for('login'))
    
    # 将用户ID添加到请求上下文中，方便后续使用
    request.user_id = user_id
    
    return None

# 应用全局登录验证
app.before_request(before_request_handler)

# 配置MySQL数据库
# 请根据您的MySQL服务器配置修改以下参数
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+pymysql://root:123456@localhost:3306/mobile_test_platform'  # 密码连接
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['SQLALCHEMY_ECHO'] = False  # 设置为True可以查看SQL语句

# 初始化数据库
db.init_app(app)

# 创建脚本存储目录
script_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'scripts')
os.makedirs(script_dir, exist_ok=True)

# 将项目根目录添加到Python路径
sys.path.append(os.path.dirname(os.path.abspath(__file__)))

# 导入模块
from backend.models.device import Device
from backend.models.task import Task
from backend.models.script import Script
from backend.services.device_manager.device_manager import DeviceManager
from backend.services.task_scheduler.task_scheduler import TaskScheduler
from backend.services.script_manager.script_manager import ScriptManager
from websocket_service.websocket_service import WebSocketService

# 初始化服务实例
device_manager = DeviceManager()
task_scheduler = TaskScheduler()
script_manager = ScriptManager()
websocket_service = WebSocketService()

# 创建数据库表
with app.app_context():
    db.create_all()

# 登录页面路由
@app.route('/login')
def login():
    # 如果已经登录，跳转到首页
    token = request.cookies.get('token')
    if token:
        # 验证token（简化实现）
        return redirect(url_for('index'))
    return render_template('login.html')

# 注册页面路由
@app.route('/register')
def register():
    return render_template('register.html')

# 首页路由
@app.route('/')
def index():
    # 已由全局过滤器验证登录状态
    return render_template('index.html')

# 用于测试的路由（实际项目中可以删除）
@app.route('/api/test_protected')
def test_protected():
    return jsonify({'success': True, 'message': '这是一个需要登录才能访问的API', 'user_id': request.user_id})

# 登出API
@app.route('/api/logout', methods=['POST'])
def logout():
    # 获取token
    token = request.cookies.get('token')
    if token and token in user_tokens:
        # 从字典中删除token
        del user_tokens[token]
    
    # 创建响应对象
    response = jsonify({'success': True, 'message': '登出成功'})
    
    # 清除cookie
    response.delete_cookie('token')
    
    return response

# 登录API
@app.route('/api/login', methods=['POST'])
def api_login():
    data = request.json
    phone = data.get('phone')
    password = data.get('password')
    
    # 验证输入
    if not phone or not password:
        return jsonify({'success': False, 'message': '手机号和密码不能为空'})
    
    # 查找用户
    user = User.query.filter_by(phone=phone).first()
    if not user or not user.check_password(password):
        return jsonify({'success': False, 'message': '手机号或密码错误'})
    
    # 更新最后登录时间
    user.last_login_at = datetime.utcnow()
    db.session.commit()
    
    # 生成token（简化实现）
    token = user.generate_token()
    
    # 存储token和用户ID的映射关系
    user_tokens[token] = user.id
    
    # 创建响应对象
    response = jsonify({'success': True, 'token': token, 'user': {
        'id': user.id,
        'phone': user.phone,
        'username': user.username,
        'avatar': user.avatar,
        'permission_level': user.permission_level
    }})
    
    # 设置cookie（实际项目中应设置合适的过期时间和安全选项）
    response.set_cookie('token', token)
    
    return response

# 注册API
@app.route('/api/register', methods=['POST'])
def api_register():
    data = request.json
    phone = data.get('phone')
    verification_code = data.get('verificationCode')
    password = data.get('password')
    
    # 验证输入
    if not phone or not verification_code or not password:
        return jsonify({'success': False, 'message': '请填写完整信息'})
    
    # 验证验证码
    if phone not in verification_codes or verification_codes[phone]['code'] != verification_code:
        return jsonify({'success': False, 'message': '验证码错误'})
    
    # 检查验证码是否过期（5分钟内有效）
    if time.time() - verification_codes[phone]['timestamp'] > 300:
        return jsonify({'success': False, 'message': '验证码已过期'})
    
    # 检查用户是否已存在
    if User.query.filter_by(phone=phone).first():
        return jsonify({'success': False, 'message': '该手机号已注册'})
    
    # 创建新用户
    user = User(phone=phone)
    user.set_password(password)
    db.session.add(user)
    db.session.commit()
    
    # 注册成功后删除验证码
    del verification_codes[phone]
    
    return jsonify({'success': True, 'message': '注册成功'})

# 发送验证码API
@app.route('/api/send_verification_code', methods=['POST'])
def send_verification_code():
    data = request.json
    phone = data.get('phone')
    
    if not phone:
        return jsonify({'success': False, 'message': '请输入手机号'})
    
    # 检查用户是否已存在
    if User.query.filter_by(phone=phone).first():
        return jsonify({'success': False, 'message': '该手机号已注册'})
    
    # 生成验证码
    code = ''.join(random.choices(string.digits, k=6))
    
    # 存储验证码（实际项目中应使用Redis等存储）
    verification_codes[phone] = {
        'code': code,
        'timestamp': time.time()
    }
    
    # 实际项目中应该调用短信服务发送验证码
    print(f"发送验证码 {code} 到手机号 {phone}")
    
    return jsonify({'success': True, 'message': '验证码已发送'})

# 微信扫码登录API（获取二维码）
@app.route('/api/wechat/qrcode', methods=['GET'])
def get_wechat_qrcode():
    # 生成唯一ID用于标识扫码会话
    session_id = ''.join(random.choices(string.ascii_letters + string.digits, k=32))
    
    # 存储会话信息
    wechat_login_sessions[session_id] = {
        'status': 'pending',
        'timestamp': time.time()
    }
    
    # 实际项目中应该调用微信接口获取二维码
    # 这里返回模拟的二维码URL
    qrcode_url = f"/static/images/qrcode-placeholder.png?session_id={session_id}"
    
    return jsonify({
        'success': True,
        'session_id': session_id,
        'qrcode_url': qrcode_url
    })

# 模拟微信登录回调API（实际项目中应该是微信服务器调用这个接口）
@app.route('/api/wechat/callback', methods=['POST'])
def wechat_login_callback():
    # 注意：在实际项目中，这个接口应该只允许微信服务器调用
    data = request.json
    session_id = data.get('session_id')
    openid = data.get('openid')
    
    if not session_id or not openid:
        return jsonify({'success': False, 'message': '参数错误'})
    
    if session_id not in wechat_login_sessions:
        return jsonify({'success': False, 'message': '无效的会话ID'})
    
    # 查找或创建用户
    user = User.query.filter_by(wechat_openid=openid).first()
    if not user:
        # 创建新用户（在实际项目中，应该从微信获取更多用户信息）
        user = User(phone=f'wechat_{openid[:10]}')  # 使用openid的一部分作为临时手机号
        user.username = '微信用户'
        user.wechat_openid = openid
        user.set_password(str(uuid.uuid4()))  # 设置随机密码
        db.session.add(user)
        db.session.commit()
    
    # 生成token
    token = user.generate_token()
    user_tokens[token] = user.id
    
    # 更新用户最后登录时间
    user.last_login_at = datetime.utcnow()
    db.session.commit()
    
    # 更新会话状态
    wechat_login_sessions[session_id]['status'] = 'confirmed'
    wechat_login_sessions[session_id]['user_info'] = {
        'token': token,
        'user_id': user.id,
        'username': user.username,
        'avatar': user.avatar,
        'permission_level': user.permission_level
    }
    
    return jsonify({'success': True, 'message': '登录成功'})

# 微信扫码登录状态查询API
@app.route('/api/wechat/login_status/<session_id>', methods=['GET'])
def check_wechat_login_status(session_id):
    if session_id not in wechat_login_sessions:
        return jsonify({'success': False, 'message': '无效的会话ID'})
    
    session = wechat_login_sessions[session_id]
    
    # 检查会话是否过期（5分钟）
    if time.time() - session['timestamp'] > 300:
        del wechat_login_sessions[session_id]
        return jsonify({'success': False, 'message': '会话已过期'})
    
    # 实际项目中这里应该检查微信扫码状态
    # 这里简化处理，返回模拟状态
    status = session['status']
    
    return jsonify({
        'success': True,
        'status': status,
        'user_info': session.get('user_info')
    })

# 设备管理API
@app.route('/api/devices', methods=['GET'])
def get_devices():
    devices = Device.query.all()
    return jsonify({
        'devices': [{
            'id': device.id,
            'device_id': device.device_id,
            'device_name': device.device_name,
            'model': device.model,
            'brand': device.brand,
            'os_version': device.os_version,
            'status': device.status,
            'battery_level': device.battery_level,
            'is_usb_connected': device.is_usb_connected,
            'current_task_id': device.current_task_id,
            'created_at': device.created_at.isoformat() if device.created_at else None,
            'updated_at': device.updated_at.isoformat() if device.updated_at else None
        } for device in devices]
    })

@app.route('/api/devices/<device_id>', methods=['GET'])
def get_device(device_id):
    device = device_manager.get_device_by_id(device_id)
    if not device:
        return jsonify({'error': 'Device not found'}), 404
    return jsonify({
        'id': device.id,
        'device_id': device.device_id,
        'device_name': device.device_name,
        'model': device.model,
        'brand': device.brand,
        'os_version': device.os_version,
        'status': device.status,
        'battery_level': device.battery_level,
        'is_usb_connected': device.is_usb_connected,
        'current_task_id': device.current_task_id,
        'created_at': device.created_at.isoformat() if device.created_at else None,
        'updated_at': device.updated_at.isoformat() if device.updated_at else None
    })

@app.route('/api/devices/available', methods=['GET'])
def get_available_devices():
    devices = device_manager.get_available_devices()
    return jsonify({
        'devices': [{
            'id': device.id,
            'device_id': device.device_id,
            'device_name': device.device_name,
            'model': device.model,
            'brand': device.brand,
            'os_version': device.os_version,
            'battery_level': device.battery_level
        } for device in devices]
    })

@app.route('/api/devices/<device_id>/command', methods=['POST'])
def execute_device_command(device_id):
    data = request.json
    command = data.get('command')
    if not command:
        return jsonify({'error': 'Command is required'}), 400
    
    result = device_manager.execute_command(device_id, command)
    # 确保结果格式一致
    if isinstance(result, dict) and 'result' not in result:
        return jsonify({'result': result})
    return jsonify(result)

# 任务管理API
@app.route('/api/tasks', methods=['GET'])
def get_tasks():
    tasks = task_scheduler.get_all_tasks()
    return jsonify({
        'tasks': [{
            'id': task.id,
            'name': task.name,
            'description': task.description,
            'script_id': task.script_id,
            'status': task.status,
            'priority': task.priority,
            'start_time': task.start_time.isoformat() if task.start_time else None,
            'end_time': task.end_time.isoformat() if task.end_time else None,
            'created_by': task.created_by,
            'created_at': task.created_at.isoformat() if task.created_at else None,
            'updated_at': task.updated_at.isoformat() if task.updated_at else None
        } for task in tasks]
    })

@app.route('/api/tasks/<int:task_id>', methods=['GET'])
def get_task(task_id):
    task = task_scheduler.get_task_by_id(task_id)
    if not task:
        return jsonify({'error': 'Task not found'}), 404
    return jsonify({
        'id': task.id,
        'name': task.name,
        'description': task.description,
        'script_id': task.script_id,
        'status': task.status,
        'priority': task.priority,
        'start_time': task.start_time.isoformat() if task.start_time else None,
        'end_time': task.end_time.isoformat() if task.end_time else None,
        'created_by': task.created_by,
        'created_at': task.created_at.isoformat() if task.created_at else None,
        'updated_at': task.updated_at.isoformat() if task.updated_at else None
    })

@app.route('/api/tasks', methods=['POST'])
def create_task():
    data = request.json
    name = data.get('name')
    description = data.get('description')
    script_id = data.get('script_id')
    priority = data.get('priority', 1)
    created_by = data.get('created_by', 'system')
    
    if not name or not script_id:
        return jsonify({'error': 'Name and script_id are required'}), 400
    
    task = task_scheduler.create_task(name, description, script_id, priority, created_by)
    if not task:
        return jsonify({'error': 'Failed to create task'}), 500
    
    return jsonify({
        'id': task.id,
        'name': task.name,
        'status': task.status
    }), 201

@app.route('/api/tasks/<int:task_id>/cancel', methods=['POST'])
def cancel_task(task_id):
    success = task_scheduler.cancel_task(task_id)
    if not success:
        return jsonify({'error': 'Failed to cancel task'}), 500
    return jsonify({'success': True})

# 脚本管理API
@app.route('/api/scripts', methods=['GET'])
def get_scripts():
    created_by = request.args.get('created_by')
    is_public = request.args.get('is_public', 'false').lower() == 'true'
    scripts = script_manager.get_all_scripts(created_by, is_public)
    return jsonify({
        'scripts': [{
            'id': script.id,
            'name': script.name,
            'description': script.description,
            'language': script.language,
            'created_by': script.created_by,
            'is_public': script.is_public,
            'version': script.version,
            'created_at': script.created_at.isoformat() if script.created_at else None,
            'updated_at': script.updated_at.isoformat() if script.updated_at else None
        } for script in scripts]
    })

@app.route('/api/scripts/<int:script_id>', methods=['GET'])
def get_script(script_id):
    script = script_manager.get_script_by_id(script_id)
    if not script:
        return jsonify({'error': 'Script not found'}), 404
    return jsonify({
        'id': script.id,
        'name': script.name,
        'description': script.description,
        'content': script.content,
        'language': script.language,
        'created_by': script.created_by,
        'is_public': script.is_public,
        'version': script.version,
        'created_at': script.created_at.isoformat() if script.created_at else None,
        'updated_at': script.updated_at.isoformat() if script.updated_at else None
    })

@app.route('/api/scripts', methods=['POST'])
def create_script():
    data = request.json
    name = data.get('name')
    description = data.get('description')
    content = data.get('content')
    language = data.get('language', 'python')
    created_by = data.get('created_by', 'system')
    is_public = data.get('is_public', False)
    
    if not name or not content:
        return jsonify({'error': 'Name and content are required'}), 400
    
    script = script_manager.create_script(name, description, content, language, created_by, is_public)
    if not script:
        return jsonify({'error': 'Failed to create script'}), 500
    
    return jsonify({
        'id': script.id,
        'name': script.name
    }), 201

@app.route('/api/scripts/<int:script_id>', methods=['PUT'])
def update_script(script_id):
    data = request.json
    name = data.get('name')
    description = data.get('description')
    content = data.get('content')
    language = data.get('language')
    is_public = data.get('is_public')
    
    script = script_manager.update_script(script_id, name, description, content, language, is_public)
    if not script:
        return jsonify({'error': 'Failed to update script'}), 500
    
    return jsonify({'success': True})

@app.route('/api/scripts/<int:script_id>', methods=['DELETE'])
def delete_script(script_id):
    success = script_manager.delete_script(script_id)
    if not success:
        return jsonify({'error': 'Failed to delete script'}), 500
    return jsonify({'success': True})

@app.route('/api/scripts/upload', methods=['POST'])
def upload_script():
    if 'file' not in request.files:
        return jsonify({'error': 'No file part'}), 400
    
    file = request.files['file']
    if file.filename == '':
        return jsonify({'error': 'No selected file'}), 400
    
    description = request.form.get('description')
    language = request.form.get('language', 'python')
    created_by = request.form.get('created_by', 'system')
    is_public = request.form.get('is_public', 'false').lower() == 'true'
    
    script = script_manager.upload_script(file, description, language, created_by, is_public)
    if not script:
        return jsonify({'error': 'Failed to upload script'}), 500
    
    return jsonify({
        'id': script.id,
        'name': script.name
    }), 201

# 一机多控API
@app.route('/api/multi-control', methods=['POST'])
def multi_control():
    data = request.json
    device_ids = data.get('device_ids')
    command_type = data.get('type')
    command_data = data.get('data')
    
    if not device_ids or not command_type or not command_data:
        return jsonify({'error': 'device_ids, type, and data are required'}), 400
    
    results = websocket_service.execute_multi_device_command(device_ids, command_type, command_data)
    return jsonify(results)

# 启动WebSocket服务的函数
def start_websocket_service():
    loop = asyncio.new_event_loop()
    asyncio.set_event_loop(loop)
    loop.run_until_complete(websocket_service.start(port=9002))

# 静态文件服务
@app.route('/static/<path:filename>')
def serve_static(filename):
    return send_from_directory('static', filename)

if __name__ == '__main__':
    # 启动设备监控
    device_manager.start_monitor()
    
    # 启动任务调度器
    task_scheduler.start()
    
    # 在后台线程中启动WebSocket服务
    ws_thread = threading.Thread(target=start_websocket_service, daemon=True)
    ws_thread.start()
    
    # 启动Flask应用
    app.run(host='0.0.0.0', port=5000, debug=True)